package com.download.manager;

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;

import com.download.manager.base.BaseLoadTask;
import com.download.manager.interfaces.OnDownLoadTaskCallBack;
import com.download.manager.interfaces.OnSingleFileLoadCallBack;
import com.download.manager.utils.JustDownFileLoadUtil;
import com.download.manager.utils.M3u8FileUtil;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class DownLoadTask extends BaseLoadTask {

    private int loadIndex=0;
    //m3u8下载地址
    private String loadUrl;
    //m3u8地址集合
    private List<String> m3u8List = new ArrayList<>();
    //ts文件地址集合额
    private List<String> tsList = new ArrayList<>();
    //ts文件对应的时间进度,当前项目对应的之前的所有的时间片之和
    private Map<Integer,Double> tsDurations = new HashMap<>();
    //总的时间长度
    private double duration=0;
    //m3u8的网络地址前缀
    private String httpTop;

    //当前m3u8的存储路径
    private String parentFile;
    //原始的m3u8链接
    private String initialUrl;
    //是否未最初的m3u8下载task
    private boolean isInitial=false;
    //文件地址
    private String saveFilePath;

    private DecimalFormat dateFormat=new DecimalFormat("0.00");
    private DownLoadTask childDownLoadTask;

    private boolean keepLoading=true;
    private String currentLoadTs;

    public void setInitialUrl(String initialUrl,boolean isInitial,String parentFile) {
        this.initialUrl = initialUrl;
        this.isInitial=isInitial;
        this.parentFile=parentFile;
    }

    /**
     * 停止下载
     */
    @Override
    public void stopTask(){
        stopLoadTask=true;
        if(childDownLoadTask!=null){
            childDownLoadTask.stopTask();
        }
    }

    @Override
    public void execute(String... strings){
        if(strings==null || strings.length==0){
            return ;
        }
        loadM3u8ByUrl(strings[0]);
    }

    /**
     * 提取m3u8文件信息
     * @param url
     */
    private void loadM3u8ByUrl(String url){
        loadIndex=0;
        loadUrl=url;
        httpTop=loadUrl.substring(0, loadUrl.lastIndexOf("/") + 1);
        if(TextUtils.isEmpty(loadUrl) || !loadUrl.contains(".m3u8")){
            LogMessage("url error","链接格式有误:"+loadUrl);
            downLoadError(url);
        }else {
            checkURlBTS(loadUrl);
        }
    }

    /**
     * 解析m3u8,下载m3u8文件,校验已下载文件的完整性
     * @param url 传入m3u8的地址
     */
    public void checkURlBTS(final String url) {
        try {
            if(childTaskCallBackInfo!=null){
                childTaskCallBackInfo.onStartLoad(initialUrl,url);
            }
            String m3u8name= M3u8FileUtil.getM3u8Name(url);
            final File file=new File(M3u8DownLoadManager.getInstance().SAVE_DIRS,m3u8name);
            if(!file.exists()){
                file.mkdirs();
            }
            parentFile=file.getPath();
            final File m3u8=new File(file.getPath()+"/"+m3u8name.substring(m3u8name.lastIndexOf("/")));
            if(m3u8.exists() && m3u8.length()>10){
                analyzingLoadM3u8File(m3u8);
            }else {
                m3u8.createNewFile();
                downLoadM3u8(m3u8,url);
            }
        } catch (Exception e) {
            e.printStackTrace();
            downLoadError(e.getMessage());
        }
    }

    /**
     * 下载m3u8文件
     * @param saveFile
     * @param url
     */
    private void downLoadM3u8(final File saveFile,final String url){
        JustDownFileLoadUtil.getInstance().downLoadFile(saveFile.getPath(), url, new OnSingleFileLoadCallBack() {
            @Override
            public void onLoadProgress(double progress, double duration) {
                LogMessage("m3u8load progress",progress+"  "+duration);
            }

            @Override
            public void onLoadSuccess(String filePath) {
                LogMessage("m3u8load success","load success");
                M3u8DownLoadManager.getInstance().removeSingleFileLoadCallBack(url);
                analyzingLoadM3u8File(saveFile);
            }

            @Override
            public void onLoadFail(String error) {
                LogMessage("m3u8load error",error);
                M3u8DownLoadManager.getInstance().removeSingleFileLoadCallBack(url);
                if(saveFile.exists()){
                    saveFile.delete();
                }
                keepLoading=false;
                downLoadError(url);
            }
        });
    }

    /**
     * 读取m3u8的流
     * @param file 当前需要保存或者已存在的缓存文件位置
     */
    private void analyzingLoadM3u8File(final File file) {
        try {
            InputStream inputStream = new FileInputStream(file);
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            //设置对应的缓存文件用于本地播放
            File cacheFile=new File(file.getParent(), M3u8DownLoadManager.getInstance().CACHE_FILE_HEADER+file.getName());
            if(cacheFile.exists()){
                cacheFile.delete();
            }
            FileOutputStream outputM3u8 = new FileOutputStream(cacheFile);
            BufferedWriter fileWriter=new BufferedWriter(new OutputStreamWriter(outputM3u8));
            saveFilePath=cacheFile.getPath();
            String line = "";
            while ((line = reader.readLine()) != null) {
                if (line.contains("?")) {
                    //去除链接最后的参数,使得本地播放时文件参数名合法
                    fileWriter.write(line.substring(0, line.indexOf("?")));
                } else {
                    fileWriter.write(line);
                }
//                fileWriter.write(subTsNoName(line));
                //换行无效,可删除
                outputM3u8.write(System.getProperty("line.separator").getBytes());
                if (line.startsWith("#")) {
                    //获取ts片对应的时间信息
                    analyzingTsTime(line);
                } else if (line.length() > 0 && (line.contains(".m3u8") || line.endsWith(".m3u8"))) {
                    m3u8List.add(httpTop+line);
                } else if (line.length() > 0 && (line.contains(".ts") || line.endsWith(".ts"))) {//判断该文件是否含有.ts文件
                    tsList.add(line);
                }
            }
            fileWriter.flush();
            outputM3u8.flush();
            inputStream.close();
            outputM3u8.close();
            fileWriter.close();
            loadIndex=0;
            if(childTaskCallBackInfo!=null){
                childTaskCallBackInfo.onM3u8SaveFile(initialUrl,loadUrl,saveFilePath,isInitial);
            }
            //如果还有m3u8文件则开启循环任务
            if (m3u8List != null && m3u8List.size() != 0) {
                downLoadAutoM3u8(cacheFile.getParent());
            }
            if(tsList.size()!=0){
                if(childTaskCallBackInfo!=null){
                    childTaskCallBackInfo.onDuration(initialUrl,loadUrl,duration);
                }
                analyzingTsToDownLoad(tsList.get(0));
            }else {
                LogMessage("load error","TS无下载内容");
            }
        }catch (Exception e){
            e.printStackTrace();
            downLoadError(e.getMessage());
        }

    }

    private String subTsNoName(String line){
        String newLine=line;
//        if(newLine.contains(".ts")) {
//            newLine.replace(".ts","");
//        }
        if (newLine.contains("?")) {
            //去除链接最后的参数,使得本地播放时文件参数名合法
            return  newLine.substring(0, newLine.indexOf("?"));
        } else {
            return newLine;
        }
    }

    /**
     * 当m3u8文件中有多条m3u8子链接的时候优先下载中间选项
     */
    private void downLoadAutoM3u8(String filePath){
        String nextM3u8=m3u8List.get(0);
        if(m3u8List.size()>=2){
            nextM3u8=m3u8List.get(1);
        }
//        String child=nextM3u8.replace(httpTop,"");
        String childName= M3u8FileUtil.getM3u8Name(nextM3u8);
//        String parent=filePath+"/"+nextM3u8.replace(childName,"");
//        if(parent.endsWith("/")) parent=parent.substring(0,parent.lastIndexOf("/"));
        File nextfile=new File(M3u8DownLoadManager.getInstance().SAVE_DIRS,childName);
        if(!nextfile.exists()){
            nextfile.mkdirs();
        }
        childDownLoadTask=new DownLoadTask();
        childDownLoadTask.setInitialUrl(initialUrl,false,nextfile.getPath());
        childDownLoadTask.setChildTaskCallBackInfo(onTaskChildTaskCallBackInfo);
        childDownLoadTask.execute(nextM3u8);
    }

    /**
     * 解析ts链接
     */
    public void analyzingTsToDownLoad(final String tsUrl) {
        String path = M3u8FileUtil.getTsPath(tsUrl);
        String fileName = M3u8FileUtil.getTsName(tsUrl);
        try {
            String dir = parentFile + path;
            String pathName = dir + "/" + fileName;//文件存储路径
            File parentFile=new File(dir);
            if(!parentFile.exists()){
                parentFile.mkdirs();
            }
//            pathName=pathName.replace(".ts","");
            final File file = new File(pathName);
            if (file.exists()) {
                //已存在不重复下载
                LogMessage("######### has save",httpTop+tsUrl);
                sendLoadSize(true,httpTop+tsUrl,0,true,file);
            } else {
                LogMessage("######### new down load",httpTop+tsUrl);
                LogMessage("######### new down load save",file.getPath());
                downLoadTsToLocate(file,httpTop+tsUrl);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存ts到本地
     * @param file
     * @param tsUrl
     */
    private void downLoadTsToLocate(final File file,final String tsUrl){
        currentLoadTs=tsUrl;
        JustDownFileLoadUtil.getInstance().downLoadFile(file.getPath(), tsUrl, new OnSingleFileLoadCallBack() {
            @Override
            public void onLoadProgress(double progress, double duration) {
                if(duration==0){
                    sendLoadSize(false, tsUrl,0,false,file);
                }else {
                    try {
                        sendLoadSize(false, tsUrl,
                                progress/duration,
                                false,file);
                    }catch (Exception e){

                    }
                }
            }

            @Override
            public void onLoadSuccess(String filePath) {
                LogMessage("downLoadTsToLocate","onLoadSuccess "+filePath);
                LogMessage("#########","onLoadSuccess : index="+loadIndex+"  "+tsList.size());
                M3u8DownLoadManager.getInstance().removeSingleFileLoadCallBack(tsUrl);
                sendLoadSize(true,tsUrl,0,true,file);
            }

            @Override
            public void onLoadFail(String error) {
                LogMessage("######### downLoadTsToLocate","onLoadFail "+error);
                M3u8DownLoadManager.getInstance().removeSingleFileLoadCallBack(tsUrl);
                if(file.exists()){
                    file.delete();
                }
                sendLoadSize(true,tsUrl,0,false,file);
            }
        });
    }


    /**
     * 解析ts对应的时间
     * @param line
     */
    private void analyzingTsTime(String line){
        try{
            if(line.contains(":")&&line.endsWith(",")){
                String num=line.substring(line.indexOf(":")+1,line.indexOf(","));
                double ts=0;
                if(!TextUtils.isEmpty(num) && !num.equals("Infinity")) {
                    ts = Objects.requireNonNull(dateFormat.parse(num)).doubleValue();
                }
                duration+=ts;
                tsDurations.put(loadIndex,duration);
                loadIndex++;
            }
        }catch (Exception e){

        }
    }

    /**
     * 发送大小数据
     * @param next 是否执行下一片下载
     * @param tsUrl 当前下载的ts的链接
     * @param cache 缓存的大小
     * @param success 是否下载成功
     * @param file ts保存的文件
     */
    @SuppressLint("LongLogTag")
    private void sendLoadSize(boolean next, String tsUrl, double cache,boolean success,File file){
        double progress=0;
        if(next) {
            if(loadIndex<tsDurations.size()){
                progress=tsDurations.get(loadIndex);
            }else {
                progress=tsDurations.get(tsDurations.size()-1);
            }
            if (tsDurations.size() > loadIndex) {
                if(childTaskCallBackInfo!=null){
                    childTaskCallBackInfo.onCanPlayProgress(initialUrl,loadUrl,progress);
                    childTaskCallBackInfo.onLoadProgress(initialUrl,loadUrl,progress,duration);
                }
            }
            if(loadIndex<tsList.size()-1 && !stopLoadTask){
                loadIndex++;
                tsDownLoadSuccessCallBack(file);
                analyzingTsToDownLoad(tsList.get(loadIndex));
            }else {
                if (childTaskCallBackInfo != null) {
                    keepLoading=false;
                    if (loadIndex >= tsList.size() - 1) {
                        childTaskCallBackInfo.onDownLoadFinish(initialUrl,loadUrl,true);
                    } else {
                        childTaskCallBackInfo.onDownLoadFinish(initialUrl,loadUrl,false);
                    }
                }
            }
        }else {
            if(stopLoadTask){
                if(!TextUtils.isEmpty(currentLoadTs)){
                    M3u8DownLoadManager.getInstance().removeSingleFileLoadCallBack(currentLoadTs);
                }
                if(childTaskCallBackInfo!=null){
                    childTaskCallBackInfo.onPauseDownLoad(initialUrl, loadUrl);
                }
            }else {
                if (loadIndex > 0) {
                    progress = tsDurations.get(loadIndex - 1);
                }
                if (childTaskCallBackInfo != null) {
                    childTaskCallBackInfo.onCanPlayProgress(initialUrl, loadUrl, progress);
                    childTaskCallBackInfo.onLoadProgress(initialUrl, loadUrl,
                            progress * (1 - cache) + tsDurations.get(loadIndex) * cache, duration);
                }
            }
        }

    }

    /**
     * ts 保存成功或者已存在的进度数据
     * @param file
     */
    private void tsDownLoadSuccessCallBack(File file){
        //ts开始下载第几片
        if(childTaskCallBackInfo!=null){
            childTaskCallBackInfo.onTSSaveFile(initialUrl,loadUrl,file.getPath());
            childTaskCallBackInfo.onLoadTsDuration(initialUrl,loadUrl,tsList.size()-1);
            childTaskCallBackInfo.onLoadTsProgress(initialUrl,loadUrl,loadIndex,tsList.size()-1);
        }
    }


    private void LogMessage(String key,String value){
        Log.d(key,value);
    }

    private OnDownLoadTaskCallBack onTaskChildTaskCallBackInfo=new OnDownLoadTaskCallBack(){

        @Override
        public void onDuration(String initialUrl, String key, double duration) {
            sendInfoToParent(0,initialUrl,key,0,duration);
        }

        @Override
        public void onLoadProgress(String initialUrl, String key, double progress, double duration) {
            sendInfoToParent(1,initialUrl,key,progress,duration);
        }

        @Override
        public void onLoadTsProgress(String initialUrl, String key, int progress, double duration) {
            sendInfoToParent(2,initialUrl,key,progress,duration);
        }

        @Override
        public void onLoadTsDuration(String initialUrl, String key, int duration) {
            sendInfoToParent(3,initialUrl,key,0,duration);
        }

        @Override
        public void onCanPlayProgress(String initialUrl, String key, double progress) {
            sendInfoToParent(4,initialUrl,key,progress,0);
        }

        @Override
        public void onM3u8SaveFile(String initialUrl, String key, String fileName, boolean isInitial) {
            sendInfoToParent(5,initialUrl,key,true,fileName);
        }

        @Override
        public void onTSSaveFile(String initialUrl, String key, String fileName) {
            sendInfoToParent(6,initialUrl,key,true,fileName);
        }

        @Override
        public void onDownLoadFinish(String initialUrl, String key, boolean success) {
            sendInfoToParent(7,initialUrl,key,success,null);
        }

        @Override
        public void onDownLoadError(String initialUrl, String key, boolean success) {
            sendInfoToParent(8,initialUrl,key,success,null);
        }

        @Override
        public void onPauseDownLoad(String initialUrl, String key) {
            if(childTaskCallBackInfo!=null){
                childTaskCallBackInfo.onPauseDownLoad(initialUrl, key);
            }
        }

        @Override
        public void onStartLoad(String initialUrl, String key) {
            if(childTaskCallBackInfo!=null){
                childTaskCallBackInfo.onStartLoad(initialUrl, key);
            }
        }
    };

    private void sendInfoToParent(int type,String initialUrl,String key,double progress,double duration){
        if(childTaskCallBackInfo!=null){
            switch (type){
                case 0:
                    childTaskCallBackInfo.onDuration(initialUrl, key, duration);
                    break;
                case 1:
                    childTaskCallBackInfo.onLoadProgress(initialUrl, key, progress, duration);
                    break;
                case 2:
                    childTaskCallBackInfo.onLoadTsProgress(initialUrl,key,(int)progress,duration);
                    break;
                case 3:
                    childTaskCallBackInfo.onLoadTsDuration(initialUrl,key,(int)duration);
                    break;
                case 4:
                    childTaskCallBackInfo.onCanPlayProgress(initialUrl, key, progress);
                    break;

            }
        }
    }

    private void sendInfoToParent(int type,String initialUrl,String key,boolean success,String fileName){
        if(childTaskCallBackInfo!=null){
            switch (type){
                case 5:
                    childTaskCallBackInfo.onM3u8SaveFile(initialUrl,key,fileName,true);
                    break;
                case 6:
                    childTaskCallBackInfo.onTSSaveFile(initialUrl, key, fileName);
                    break;
                case 7:
                    childTaskCallBackInfo.onDownLoadFinish(initialUrl, key, success);
                    break;
                case 8:
                    childTaskCallBackInfo.onDownLoadError(initialUrl, key, success);
                    break;
            }
        }
    }

    private void downLoadError(String key){
        if(childTaskCallBackInfo!=null){
            childTaskCallBackInfo.onDownLoadError(initialUrl,key,false);
        }
    }

}

